-- 自举
local function bootstrap()
	-- 设置操作系统的路径分隔符 os.dirsep, C语言扩展模块的文件扩展名 os.cmodext
	local _, _, dirsep, cmodext = package.cpath:find("%.([\\/])?.(%w+);");
	os.dirsep, os.cmodext = dirsep, cmodext;
	-- 取得trunk路径(即当前运行的脚本文件路径,有可能是相对路径)，并保存到 os.trunkPath
	local path = string.sub(debug.getinfo(2).source, 2);
	os.trunkPath = string.match(path, "^(.*)" .. os.dirsep);
	-- 把trunk路径添加到包查找路径最前面
	package.path = string.format("%s%s?.lua;%s", os.trunkPath, os.dirsep, package.path);
	package.cpath = string.format("%s%s?.%s;%s", os.trunkPath, os.dirsep, os.cmodext, package.cpath);
end
bootstrap();

-- 加载包要放到自举之后，否则有可能因为路径问题找不到包
local zylib = require("zylib")
local rs232 = require("rs232.core")
local phy6212 = require("phy6212")

-- 监听串口并使用 stdin 交互
local function monitorAtStdin(port)
    local ok, err, len, ch, key;
    io.write("\r\nMonitor started ! Press Ctrl+c to stop monitor ...\r\n\r\n");

    while true do
		-- 从串口每次读100个字节，超时时间1ms
		-- 以保护模式运行，以便监听 Ctrl+c 退出
		-- err, ch, len = port:read(100, 1);
		ok, err, ch, len = pcall(port.read, port, 100, 1);
		if not ok then
			if err ~= "interrupted!" then error(string.format("Port read error: %s", err)); end
            io.write("\r\n");
			break;
		elseif err == 0 then
            -- 正常读到数据，把数据写到控制台
            io.write(ch);
		elseif err ~= rs232.RS232_ERR_TIMEOUT then
			error(string.format("Port read error: %s", rs232.error_tostring(err)));
        end;
    end

    port:close();
end

-- 监听串口并使用 tty 交互
local function monitorAtTty(port, tty)
    local err, len, ch, key;
    tty:write("\r\nMonitor started ! Press Ctrl+c / Ctrl+d / Ctrl+z to stop monitor ...\r\n\r\n", 1000);

    while true do
		-- 从串口每次读100个字节，超时时间1ms
		err, ch, len = port:read(100, 1);
		if err == 0 then
            -- 正常读到数据，把数据写到控制台
            err, len = tty:write(ch, 1000);
            if err ~= 0 then error(string.format("tty write error: %s", rs232.error_tostring(err))); end
		elseif err ~= rs232.RS232_ERR_TIMEOUT then
			error(string.format("Port read error: %s", rs232.error_tostring(err)));
        end;

		-- 从控制台每次读1个字节，超时时间1ms
		err, ch, len = tty:read(1, 1);
		if err == 0 then
            -- 正常读到数据
            -- 判断是否是控制字符 0x03 - Ctrl+c; 0x04 - Ctrl+d; 0x1A - Ctrl+z
            -- 如果是的话，就表示要中止程序
            key = ch:byte(1)
            if key == 0x03 or key == 0x04 or key == 0x1A then
                tty:write("\r\n");
                break;
            end

            -- 把数据写到串口
            err, len = port:write(ch, 1000);
            if err ~= 0 then error(string.format("Port write error: %s", rs232.error_tostring(err))); end
		elseif err ~= rs232.RS232_ERR_TIMEOUT then
			error(string.format("tty read error: %s", rs232.error_tostring(err)));
		end;
    end

    port:close();
    --tty:close();
end

local function main(argc, argv)
    local err, tty, port;

	if argc < 1 then
		print("Bad parameters !");
		print("Usage:")
		print("\tmonitor /dev/ttyUSB0 `tty`");
		print("\tmonitor COM3");
		return;
	end

	port = phy6212.open(argv[1]);

	-- MCU 重启进入运行模式
	phy6212.resetToRun(port);

	if argc >= 2 then
		err, tty = rs232.open(argv[2]);
		if err ~= 0 then error(string.format("Failed to open tty '%s': %s", argv[2], rs232.error_tostring(err))); end
		monitorAtTty(port, tty);
	else
		monitorAtStdin(port);
	end
end

-- lua monitor.lua /dev/ttyUSB0 `tty`
main(#arg, arg)
